package simpledb.storage;

import simpledb.common.Type;

import java.io.Serializable;
import java.util.*;

/**
 * TupleDesc describes the schema of a tuple.
 */
public class TupleDesc implements Serializable {

    //TupleDesc

    /**
     * A help class to facilitate organizing the information of each field
     * 静态内部类  用来管理每一个字段
     */
    public static class TDItem implements Serializable {

        private static final long serialVersionUID = 1L;

        /**
         * The type of the field  Type枚举类
         */
        public final Type fieldType;

        /**
         * The name of the field   字符串类
         */
        public final String fieldName;

        public TDItem(Type t, String n) {
            this.fieldName = n;
            this.fieldType = t;
        }


        /**
         * 这边也需要重写equals方法
         *
         * @param o
         * @return
         */
        @Override
        public boolean equals(Object o) {
            // 如果引用相同，直接返回true
            if (this == o) return true;

            // 如果o不是TDItem类型，返回false
            if (o == null || getClass() != o.getClass()) return false;

            // 将o强制转换为TDItem类型
            TDItem tdItem = (TDItem) o;

            // 比较fieldType和fieldName
            return Objects.equals(fieldType, tdItem.fieldType) &&
                    Objects.equals(fieldName, tdItem.fieldName);
        }
        @Override
        public int hashCode() {
            // 使用Objects类的hash方法来生成哈希值，fieldType和fieldName为参与计算的字段
            return Objects.hash(fieldType, fieldName);
        }


        public String toString() {
            return fieldName + "(" + fieldType + ")";
        }
    }

    /**
     * @return An iterator which iterates over all the field TDItems
     * that are included in this TupleDesc
     */
    /**
     * @return An iterator which iterates over all the field TDItems that are included in this TupleDesc
     */
    public Iterator<TDItem> iterator() {
        return items.iterator();  //返回迭代器
    }

    private static final long serialVersionUID = 1L;
    private List<TDItem> items;

    /**
     * Create a new TupleDesc with typeAr.length fields with fields of the
     * specified types, with associated named fields.
     *
     * @param typeAr  array specifying the number of and types of fields in this
     *                TupleDesc. It must contain at least one entry.
     * @param fieldAr array specifying the names of the fields. Note that names may
     *                be null.
     */
    public TupleDesc(Type[] typeAr, String[] fieldAr) {
        // some code goes here
        items = new ArrayList<>();
        for (int i = 0; i < typeAr.length; i++) {
            items.add(new TDItem(typeAr[i], fieldAr[i]));
        }
    }

    /**
     * Constructor. Create a new tuple desc with typeAr.length fields with
     * fields of the specified types, with anonymous (unnamed) fields.
     *
     * @param typeAr array specifying the number of and types of fields in this
     *               TupleDesc. It must contain at least one entry.
     */
    public TupleDesc(Type[] typeAr) {
        // some code goes here
        items = new ArrayList<>();
        for (Type type : typeAr) {
            items.add(new TDItem(type, null));
        }
    }

    /**
     * @return the number of fields in this TupleDesc
     */
    public int numFields() {
        // some code goes here
        return items.size();
    }

    /**
     * Gets the (possibly null) field name of the ith field of this TupleDesc.
     *
     * @param i index of the field name to return. It must be a valid index.
     * @return the name of the ith field
     * @throws NoSuchElementException if i is not a valid field reference.
     */
    public String getFieldName(int i) throws NoSuchElementException {
        // some code goes here
        if (i > items.size()) {
            throw new NoSuchElementException("没有这个字段");
        }
        return items.get(i).fieldName;
    }

    /**
     * Gets the type of the ith field of this TupleDesc.
     *
     * @param i The index of the field to get the type of. It must be a valid
     *          index.
     * @return the type of the ith field
     * @throws NoSuchElementException if i is not a valid field reference.
     */
    public Type getFieldType(int i) throws NoSuchElementException {
        // some code goes here
        if (i > items.size()) {
            throw new NoSuchElementException("没有这个类型");
        }
        return items.get(i).fieldType;
    }

    /**
     * Find the index of the field with a given name.
     *
     * @param name name of the field.
     * @return the index of the field that is first to have the given name.
     * @throws NoSuchElementException if no field with a matching name is found.
     */
    public int fieldNameToIndex(String name) throws NoSuchElementException {
        // some code goes here
        for (int i = 0; i < items.size(); i++) {
            if (items.get(i).fieldName != null && items.get(i).fieldName.equals(name)) {
                return i;
            }
        }
        throw new NoSuchElementException("");
    }

    /**
     * @return The size (in bytes) of tuples corresponding to this TupleDesc.
     * Note that tuples from a given TupleDesc are of a fixed size.
     * type使用的字节长度    FileType的getLen()
     */
    public int getSize() {
        // some code goes here
        int size = 0;
        for (TDItem item : items) {
            size += item.fieldType.getLen();
        }
        return size;
    }

    /**
     * Merge two TupleDescs into one, with td1.numFields + td2.numFields fields,
     * with the first td1.numFields coming from td1 and the remaining from td2.
     *
     * @param td1 The TupleDesc with the first fields of the new TupleDesc
     * @param td2 The TupleDesc with the last fields of the TupleDesc
     * @return the new TupleDesc
     */
    public static TupleDesc merge(TupleDesc td1, TupleDesc td2) {
        // some code goes here
        Type[] mergedTypes = new Type[td1.numFields() + td2.numFields()];
        String[] mergedNames = new String[td1.numFields() + td2.numFields()];

        int i = 0;
        for (TDItem item : td1.items) {
            mergedTypes[i] = item.fieldType;
            mergedNames[i] = item.fieldName;
            i++;
        }

        for (TDItem item : td2.items) {
            mergedTypes[i] = item.fieldType;
            mergedNames[i] = item.fieldName;
            i++;
        }

        return new TupleDesc(mergedTypes, mergedNames);
    }

    /**
     * Compares the specified object with this TupleDesc for equality. Two
     * TupleDescs are considered equal if they have the same number of items
     * and if the i-th type in this TupleDesc is equal to the i-th type in o
     * for every i.
     *
     * @param o the Object to be compared for equality with this TupleDesc.
     * @return true if the object is equal to this TupleDesc.
     */

    public boolean equals(Object o) {
        // some code goes here
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TupleDesc tupleDesc = (TupleDesc) o;
        return items.equals(tupleDesc.items);
    }

    public int hashCode() {
        // If you want to use TupleDesc as keys for HashMap, implement this so
        // that equal objects have equals hashCode() results
        return Objects.hash(items);
    }

    /**
     * Returns a String describing this descriptor. It should be of the form
     * "fieldType[0](fieldName[0]), ..., fieldType[M](fieldName[M])", although
     * the exact format does not matter.
     *
     * @return String describing this descriptor.
     */
    public String toString() {
        // some code goes here
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < items.size(); i++) {
            sb.append(items.get(i).toString());
            if (i < items.size() - 1) {
                sb.append(", ");
            }
        }
        return sb.toString();
    }
}
